/**
 * Copyright 2007 Andrea Agili <agea@users.sf.net>
 * 
 * This plugin is licensed under the Eclipse Public License:
 * http://www.eclipse.org/org/documents/epl-v10.php
 * 
 */

package org.dnd.rcp.securetexteditor;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;


import org.dnd.rcp.securetexteditor.password.PasswordDialog;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IWorkbenchPartConstants;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.eclipse.ui.part.EditorPart;

public class SecureEditor extends EditorPart {

	public final static String ID = "SecureEditor.TextEditor";

	private String text = null;

	private boolean dirty = false;

	private ScrolledForm form = null;

	private Text formText = null;

	private File file = null;

	private String key = null;

	private String algorithm = "AES";

	private int keylen = 128;

	private String underText = "Uniat Under Text";

	@Override
	public void doSave(IProgressMonitor monitor) {
		if (file == null)
			doSaveAs();
		try {
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			text = formText.getText();
			byte[] tb = formText.getText().getBytes();
			out.write(tb, 0, tb.length);
			byte[] raw = key.getBytes();
			SecretKeySpec skeySpec = new SecretKeySpec(raw, algorithm);
			Cipher cipher = Cipher.getInstance("AES");
			cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
			int padding = out.size() % 16;
			out.write(new byte[padding], 0, padding);
			byte[] encrypted = cipher.doFinal(out.toByteArray());
			FileOutputStream fos = new FileOutputStream(file);
			fos.write(encrypted);
			fos.close();
			setDirty(false);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void doSaveAs() {
		FileDialog dialog = new FileDialog(getSite().getShell(), SWT.SAVE);
		dialog.setFilterExtensions(new String[] { "*.uniatxt" });
		String filename = dialog.open();
		file = new File(filename);
		openKeyDialog();
		doSave(null);
	}

	@Override
	public void init(IEditorSite site, IEditorInput input)
			throws PartInitException {
		setSite(site);
		setInput(input);
	}

	@Override
	public boolean isDirty() {
		return dirty;
	}

	@Override
	public boolean isSaveAsAllowed() {
		return true;
	}

	@Override
	public void createPartControl(Composite parent) {
		IEditorInput input = getEditorInput();

		IFile f = (IFile) input.getAdapter(IFile.class);
		if (f != null) {
			file = f.getLocation().toFile();
		}
		if (input instanceof SecureEditorInput) {
			SecureEditorInput fileInput = (SecureEditorInput) input;
			file = fileInput.getFile();
		}
		if (file != null) {
			setPartName(file.getName());
			setContentDescription(underText);
			openKeyDialog();
			try {
				byte[] raw = key.getBytes();
				SecretKeySpec skeySpec = new SecretKeySpec(raw, algorithm);
				Cipher cipher = Cipher.getInstance(algorithm);
				cipher.init(Cipher.DECRYPT_MODE, skeySpec);
				ByteArrayOutputStream out = new ByteArrayOutputStream();
				out.write(getBytesFromFile(file), 0, (int) file.length());
				int padding = out.size() % 16;
				out.write(new byte[padding], 0, padding);
				text = new String(cipher.doFinal(out.toByteArray()));
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		} else {
			text = "";
			setPartName("New File");
			setContentDescription(underText);
		}

		FormToolkit toolkit = new FormToolkit(parent.getDisplay());
		form = toolkit.createScrolledForm(parent);
		form.getBody().setLayout(new FillLayout());
		formText = toolkit.createText(form.getBody(), text, SWT.MULTI);
		formText.addKeyListener(new KeyListener() {
			public void keyPressed(KeyEvent e) {
			}

			public void keyReleased(KeyEvent e) {
				if (!formText.getText().equals(text)) {
					setDirty(true);
				}
			}
		});
		form.reflow(true);
	}

	private void openKeyDialog() {
		PasswordDialog dialog = new PasswordDialog(getSite().getShell(),
				file.getName() + " (" + algorithm + " - " + keylen + " bit)",
				"Insert key:", "", null);
		dialog.open();
		key = dialog.getValue();
		if (key == null || key == "") {
			throw new RuntimeException("A key must be provided");
		}
		if (key.length() < keylen / 8) {
			while (key.length() < keylen / 8) {
				key = key + " ";
			}
		}
		if (key.length() > keylen / 8) {
			key = key.substring(0, keylen / 8);
		}
	}

	@Override
	public void setFocus() {
		form.setFocus();
	}

	public void setDirty(boolean dirty) {
		this.dirty = dirty;
		firePropertyChange(IWorkbenchPartConstants.PROP_DIRTY);
	}

	// Returns the contents of the file in a byte array.
	public byte[] getBytesFromFile(File file) throws IOException {
		InputStream is = new FileInputStream(file);

		// Get the size of the file
		long length = file.length();

		// You cannot create an array using a long type.
		// It needs to be an int type.
		// Before converting to an int type, check
		// to ensure that file is not larger than Integer.MAX_VALUE.
		if (length > Integer.MAX_VALUE) {
			// File is too large
		}

		// Create the byte array to hold the data
		byte[] bytes = new byte[(int) length];

		// Read in the bytes
		int offset = 0;
		int numRead = 0;
		while (offset < bytes.length
				&& (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
			offset += numRead;
		}

		// Ensure all the bytes have been read in
		if (offset < bytes.length) {
			throw new IOException("Could not completely read file "
					+ file.getName());
		}

		// Close the input stream and return bytes
		is.close();
		return bytes;
	}
	
	
}
